#242 info: edit tags with dynamic placeholders for country / place
This commit is contained in:
parent
9fa977a7c1
commit
8ed8787c24
6 changed files with 232 additions and 50 deletions
|
@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
- Info: edit MP4 metadata (date / location / title / description / rating / tags / rotation)
|
- Info: edit MP4 metadata (date / location / title / description / rating / tags / rotation)
|
||||||
- Info: edit location by copying from other item
|
- Info: edit location by copying from other item
|
||||||
|
- Info: edit tags with dynamic placeholders for country / place
|
||||||
- Widget: option to open collection on tap
|
- Widget: option to open collection on tap
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
@ -868,6 +868,10 @@
|
||||||
"tagEditorPageNewTagFieldLabel": "New tag",
|
"tagEditorPageNewTagFieldLabel": "New tag",
|
||||||
"tagEditorPageAddTagTooltip": "Add tag",
|
"tagEditorPageAddTagTooltip": "Add tag",
|
||||||
"tagEditorSectionRecent": "Recent",
|
"tagEditorSectionRecent": "Recent",
|
||||||
|
"tagEditorSectionPlaceholders": "Placeholders",
|
||||||
|
|
||||||
|
"tagPlaceholderCountry": "Country",
|
||||||
|
"tagPlaceholderPlace": "Place",
|
||||||
|
|
||||||
"panoramaEnableSensorControl": "Enable sensor control",
|
"panoramaEnableSensorControl": "Enable sensor control",
|
||||||
"panoramaDisableSensorControl": "Disable sensor control",
|
"panoramaDisableSensorControl": "Disable sensor control",
|
||||||
|
|
95
lib/model/filters/placeholder.dart
Normal file
95
lib/model/filters/placeholder.dart
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
import 'package:aves/model/entry.dart';
|
||||||
|
import 'package:aves/model/filters/filters.dart';
|
||||||
|
import 'package:aves/model/settings/settings.dart';
|
||||||
|
import 'package:aves/theme/icons.dart';
|
||||||
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
class PlaceholderFilter extends CollectionFilter {
|
||||||
|
static const type = 'placeholder';
|
||||||
|
|
||||||
|
static const _country = 'country';
|
||||||
|
static const _place = 'place';
|
||||||
|
|
||||||
|
final String placeholder;
|
||||||
|
late final IconData _icon;
|
||||||
|
|
||||||
|
static final country = PlaceholderFilter._private(_country);
|
||||||
|
static final place = PlaceholderFilter._private(_place);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [placeholder];
|
||||||
|
|
||||||
|
PlaceholderFilter._private(this.placeholder) : super(reversed: false) {
|
||||||
|
switch (placeholder) {
|
||||||
|
case _country:
|
||||||
|
case _place:
|
||||||
|
_icon = AIcons.location;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
factory PlaceholderFilter.fromMap(Map<String, dynamic> json) {
|
||||||
|
return PlaceholderFilter._private(
|
||||||
|
json['placeholder'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toMap() => {
|
||||||
|
'type': type,
|
||||||
|
'placeholder': placeholder,
|
||||||
|
};
|
||||||
|
|
||||||
|
Future<String?> toTag(AvesEntry entry) async {
|
||||||
|
switch (placeholder) {
|
||||||
|
case _country:
|
||||||
|
case _place:
|
||||||
|
if (!entry.isCatalogued) {
|
||||||
|
await entry.catalog(background: false, force: false, persist: true);
|
||||||
|
}
|
||||||
|
if (!entry.hasGps) return null;
|
||||||
|
|
||||||
|
if (!entry.hasFineAddress) {
|
||||||
|
await entry.locate(background: false, force: false, geocoderLocale: settings.appliedLocale);
|
||||||
|
}
|
||||||
|
final address = entry.addressDetails;
|
||||||
|
if (address == null) return null;
|
||||||
|
|
||||||
|
if (placeholder == _country) return address.countryName;
|
||||||
|
if (placeholder == _place) return address.place;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
EntryFilter get positiveTest => (entry) => throw Exception('this is not a test');
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get exclusiveProp => false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get universalLabel => placeholder;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String getLabel(BuildContext context) {
|
||||||
|
switch (placeholder) {
|
||||||
|
case _country:
|
||||||
|
return context.l10n.tagPlaceholderCountry;
|
||||||
|
case _place:
|
||||||
|
return context.l10n.tagPlaceholderPlace;
|
||||||
|
default:
|
||||||
|
return placeholder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(_icon, size: size);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get category => type;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get key => '$type-$placeholder';
|
||||||
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/model/entry_metadata_edition.dart';
|
import 'package:aves/model/entry_metadata_edition.dart';
|
||||||
|
import 'package:aves/model/filters/filters.dart';
|
||||||
|
import 'package:aves/model/filters/placeholder.dart';
|
||||||
|
import 'package:aves/model/filters/tag.dart';
|
||||||
import 'package:aves/model/metadata/date_modifier.dart';
|
import 'package:aves/model/metadata/date_modifier.dart';
|
||||||
import 'package:aves/model/metadata/enums/enums.dart';
|
import 'package:aves/model/metadata/enums/enums.dart';
|
||||||
import 'package:aves/model/source/collection_lens.dart';
|
import 'package:aves/model/source/collection_lens.dart';
|
||||||
|
@ -74,17 +77,32 @@ mixin EntryEditorMixin {
|
||||||
Future<Map<AvesEntry, Set<String>>?> selectTags(BuildContext context, Set<AvesEntry> entries) async {
|
Future<Map<AvesEntry, Set<String>>?> selectTags(BuildContext context, Set<AvesEntry> entries) async {
|
||||||
if (entries.isEmpty) return null;
|
if (entries.isEmpty) return null;
|
||||||
|
|
||||||
final tagsByEntry = Map.fromEntries(entries.map((v) => MapEntry(v, v.tags.toSet())));
|
final filtersByEntry = Map.fromEntries(entries.map((v) {
|
||||||
|
// use `<CollectionFilter>{...}` instead of `toSet()` to circumvent an implicit typing issue, as of Dart v2.18.2
|
||||||
|
final filters = <CollectionFilter>{...v.tags.map(TagFilter.new)};
|
||||||
|
return MapEntry(v, filters);
|
||||||
|
}));
|
||||||
await Navigator.push(
|
await Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
settings: const RouteSettings(name: TagEditorPage.routeName),
|
settings: const RouteSettings(name: TagEditorPage.routeName),
|
||||||
builder: (context) => TagEditorPage(
|
builder: (context) => TagEditorPage(
|
||||||
tagsByEntry: tagsByEntry,
|
filtersByEntry: filtersByEntry,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final tagsByEntry = <AvesEntry, Set<String>>{};
|
||||||
|
await Future.forEach(filtersByEntry.entries, (kv) async {
|
||||||
|
final entry = kv.key;
|
||||||
|
final filters = kv.value;
|
||||||
|
final tags = filters.whereType<TagFilter>().map((v) => v.tag).toSet();
|
||||||
|
tagsByEntry[entry] = tags;
|
||||||
|
|
||||||
|
final placeholderTags = await Future.wait(filters.whereType<PlaceholderFilter>().map((v) => v.toTag(entry)));
|
||||||
|
tags.addAll(placeholderTags.whereNotNull().where((v) => v.isNotEmpty));
|
||||||
|
});
|
||||||
|
|
||||||
return tagsByEntry;
|
return tagsByEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
|
import 'package:aves/model/filters/filters.dart';
|
||||||
|
import 'package:aves/model/filters/placeholder.dart';
|
||||||
import 'package:aves/model/filters/tag.dart';
|
import 'package:aves/model/filters/tag.dart';
|
||||||
import 'package:aves/model/source/collection_source.dart';
|
import 'package:aves/model/source/collection_source.dart';
|
||||||
import 'package:aves/theme/durations.dart';
|
import 'package:aves/theme/durations.dart';
|
||||||
|
@ -9,18 +11,17 @@ import 'package:aves/widgets/common/expandable_filter_row.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
|
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
|
||||||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class TagEditorPage extends StatefulWidget {
|
class TagEditorPage extends StatefulWidget {
|
||||||
static const routeName = '/info/tag_editor';
|
static const routeName = '/info/tag_editor';
|
||||||
|
|
||||||
final Map<AvesEntry, Set<String>> tagsByEntry;
|
final Map<AvesEntry, Set<CollectionFilter>> filtersByEntry;
|
||||||
|
|
||||||
const TagEditorPage({
|
const TagEditorPage({
|
||||||
super.key,
|
super.key,
|
||||||
required this.tagsByEntry,
|
required this.filtersByEntry,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -31,14 +32,15 @@ class _TagEditorPageState extends State<TagEditorPage> {
|
||||||
final TextEditingController _newTagTextController = TextEditingController();
|
final TextEditingController _newTagTextController = TextEditingController();
|
||||||
final FocusNode _newTagTextFocusNode = FocusNode();
|
final FocusNode _newTagTextFocusNode = FocusNode();
|
||||||
final ValueNotifier<String?> _expandedSectionNotifier = ValueNotifier(null);
|
final ValueNotifier<String?> _expandedSectionNotifier = ValueNotifier(null);
|
||||||
late final List<String> _topTags;
|
late final List<CollectionFilter> _topTags;
|
||||||
|
late final List<PlaceholderFilter> _placeholders = [PlaceholderFilter.country, PlaceholderFilter.place];
|
||||||
|
|
||||||
static final List<String> _recentTags = [];
|
static final List<CollectionFilter> _recentTags = [];
|
||||||
|
|
||||||
static const Color untaggedColor = Colors.blueGrey;
|
static const Color untaggedColor = Colors.blueGrey;
|
||||||
static const int tagHistoryCount = 10;
|
static const int tagHistoryCount = 10;
|
||||||
|
|
||||||
Map<AvesEntry, Set<String>> get tagsByEntry => widget.tagsByEntry;
|
Map<AvesEntry, Set<CollectionFilter>> get tagsByEntry => widget.filtersByEntry;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -50,11 +52,11 @@ class _TagEditorPageState extends State<TagEditorPage> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final l10n = context.l10n;
|
final l10n = context.l10n;
|
||||||
final showCount = tagsByEntry.length > 1;
|
final showCount = tagsByEntry.length > 1;
|
||||||
final Map<String, int> entryCountByTag = {};
|
final Map<CollectionFilter, int> entryCountByTag = {};
|
||||||
tagsByEntry.entries.forEach((kv) {
|
tagsByEntry.entries.forEach((kv) {
|
||||||
kv.value.forEach((tag) => entryCountByTag[tag] = (entryCountByTag[tag] ?? 0) + 1);
|
kv.value.forEach((tag) => entryCountByTag[tag] = (entryCountByTag[tag] ?? 0) + 1);
|
||||||
});
|
});
|
||||||
List<MapEntry<String, int>> sortedTags = _sortEntryCountByTag(entryCountByTag);
|
List<MapEntry<CollectionFilter, int>> sortedTags = _sortEntryCountByTag(entryCountByTag);
|
||||||
|
|
||||||
return MediaQueryDataProvider(
|
return MediaQueryDataProvider(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
|
@ -76,9 +78,10 @@ class _TagEditorPageState extends State<TagEditorPage> {
|
||||||
valueListenable: _newTagTextController,
|
valueListenable: _newTagTextController,
|
||||||
builder: (context, value, child) {
|
builder: (context, value, child) {
|
||||||
final upQuery = value.text.trim().toUpperCase();
|
final upQuery = value.text.trim().toUpperCase();
|
||||||
bool containQuery(String s) => s.toUpperCase().contains(upQuery);
|
bool containQuery(CollectionFilter v) => v.getLabel(context).toUpperCase().contains(upQuery);
|
||||||
final recentFilters = _recentTags.where(containQuery).map(TagFilter.new).toList();
|
final recentFilters = _recentTags.where(containQuery).toList();
|
||||||
final topTagFilters = _topTags.where(containQuery).map(TagFilter.new).toList();
|
final topTagFilters = _topTags.where(containQuery).toList();
|
||||||
|
final placeholderFilters = _placeholders.where(containQuery).toList();
|
||||||
return ListView(
|
return ListView(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -95,7 +98,7 @@ class _TagEditorPageState extends State<TagEditorPage> {
|
||||||
),
|
),
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
onSubmitted: (newTag) {
|
onSubmitted: (newTag) {
|
||||||
_addTag(newTag);
|
_addCustomTag(newTag);
|
||||||
_newTagTextFocusNode.requestFocus();
|
_newTagTextFocusNode.requestFocus();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -105,7 +108,7 @@ class _TagEditorPageState extends State<TagEditorPage> {
|
||||||
builder: (context, value, child) {
|
builder: (context, value, child) {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
icon: const Icon(AIcons.add),
|
icon: const Icon(AIcons.add),
|
||||||
onPressed: value.text.isEmpty ? null : () => _addTag(_newTagTextController.text),
|
onPressed: value.text.isEmpty ? null : () => _addCustomTag(_newTagTextController.text),
|
||||||
tooltip: l10n.tagEditorPageAddTagTooltip,
|
tooltip: l10n.tagEditorPageAddTagTooltip,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -138,13 +141,12 @@ class _TagEditorPageState extends State<TagEditorPage> {
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
runSpacing: 8,
|
runSpacing: 8,
|
||||||
children: sortedTags.map((kv) {
|
children: sortedTags.map((kv) {
|
||||||
final tag = kv.key;
|
|
||||||
return AvesFilterChip(
|
return AvesFilterChip(
|
||||||
filter: TagFilter(tag),
|
filter: kv.key,
|
||||||
removable: true,
|
removable: true,
|
||||||
showGenericIcon: false,
|
showGenericIcon: false,
|
||||||
leadingOverride: showCount ? _TagCount(count: kv.value) : null,
|
leadingOverride: showCount ? _TagCount(count: kv.value) : null,
|
||||||
onTap: (filter) => _removeTag(tag),
|
onTap: _removeTag,
|
||||||
onLongPress: null,
|
onLongPress: null,
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
|
@ -167,6 +169,12 @@ class _TagEditorPageState extends State<TagEditorPage> {
|
||||||
expandedNotifier: _expandedSectionNotifier,
|
expandedNotifier: _expandedSectionNotifier,
|
||||||
onTap: _addTag,
|
onTap: _addTag,
|
||||||
),
|
),
|
||||||
|
_FilterRow(
|
||||||
|
title: l10n.tagEditorSectionPlaceholders,
|
||||||
|
filters: placeholderFilters,
|
||||||
|
expandedNotifier: _expandedSectionNotifier,
|
||||||
|
onTap: _addTag,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -184,49 +192,54 @@ class _TagEditorPageState extends State<TagEditorPage> {
|
||||||
visibleEntries?.forEach((entry) {
|
visibleEntries?.forEach((entry) {
|
||||||
entry.tags.forEach((tag) => entryCountByTag[tag] = (entryCountByTag[tag] ?? 0) + 1);
|
entry.tags.forEach((tag) => entryCountByTag[tag] = (entryCountByTag[tag] ?? 0) + 1);
|
||||||
});
|
});
|
||||||
List<MapEntry<String, int>> sortedTopTags = _sortEntryCountByTag(entryCountByTag);
|
List<MapEntry<CollectionFilter, int>> sortedTopTags = _sortEntryCountByTag(entryCountByTag.map((key, value) => MapEntry(TagFilter(key), value)));
|
||||||
_topTags = sortedTopTags.map((kv) => kv.key).toList();
|
_topTags = sortedTopTags.map((kv) => kv.key).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
List<MapEntry<String, int>> _sortEntryCountByTag(Map<String, int> entryCountByTag) {
|
List<MapEntry<CollectionFilter, int>> _sortEntryCountByTag(Map<CollectionFilter, int> entryCountByTag) {
|
||||||
return entryCountByTag.entries.toList()
|
return entryCountByTag.entries.toList()
|
||||||
..sort((kv1, kv2) {
|
..sort((kv1, kv2) {
|
||||||
final c = kv2.value.compareTo(kv1.value);
|
final c = kv2.value.compareTo(kv1.value);
|
||||||
return c != 0 ? c : compareAsciiUpperCaseNatural(kv1.key, kv2.key);
|
return c != 0 ? c : kv1.key.compareTo(kv2.key);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _reset() {
|
void _reset() {
|
||||||
setState(() => tagsByEntry.forEach((entry, tags) {
|
setState(() => tagsByEntry.forEach((entry, tags) {
|
||||||
|
final Set<TagFilter> originalFilters = entry.tags.map(TagFilter.new).toSet();
|
||||||
tags
|
tags
|
||||||
..clear()
|
..clear()
|
||||||
..addAll(entry.tags);
|
..addAll(originalFilters);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _addTag(String newTag) {
|
void _addCustomTag(String newTag) {
|
||||||
if (newTag.isNotEmpty) {
|
if (newTag.isNotEmpty) {
|
||||||
setState(() {
|
_addTag(TagFilter(newTag));
|
||||||
_recentTags
|
|
||||||
..remove(newTag)
|
|
||||||
..insert(0, newTag)
|
|
||||||
..removeRange(min(tagHistoryCount, _recentTags.length), _recentTags.length);
|
|
||||||
tagsByEntry.forEach((entry, tags) => tags.add(newTag));
|
|
||||||
});
|
|
||||||
_newTagTextController.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _removeTag(String tag) {
|
void _addTag(CollectionFilter newTag) {
|
||||||
setState(() => tagsByEntry.forEach((entry, tags) => tags.remove(tag)));
|
setState(() {
|
||||||
|
_recentTags
|
||||||
|
..remove(newTag)
|
||||||
|
..insert(0, newTag)
|
||||||
|
..removeRange(min(tagHistoryCount, _recentTags.length), _recentTags.length);
|
||||||
|
tagsByEntry.forEach((entry, tags) => tags.add(newTag));
|
||||||
|
});
|
||||||
|
_newTagTextController.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _removeTag(CollectionFilter filter) {
|
||||||
|
setState(() => tagsByEntry.forEach((entry, filters) => filters.remove(filter)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _FilterRow extends StatelessWidget {
|
class _FilterRow extends StatelessWidget {
|
||||||
final String title;
|
final String title;
|
||||||
final List<TagFilter> filters;
|
final List<CollectionFilter> filters;
|
||||||
final ValueNotifier<String?> expandedNotifier;
|
final ValueNotifier<String?> expandedNotifier;
|
||||||
final void Function(String tag) onTap;
|
final void Function(CollectionFilter filter) onTap;
|
||||||
|
|
||||||
const _FilterRow({
|
const _FilterRow({
|
||||||
required this.title,
|
required this.title,
|
||||||
|
@ -244,7 +257,7 @@ class _FilterRow extends StatelessWidget {
|
||||||
filters: filters,
|
filters: filters,
|
||||||
expandedNotifier: expandedNotifier,
|
expandedNotifier: expandedNotifier,
|
||||||
showGenericIcon: false,
|
showGenericIcon: false,
|
||||||
onTap: (filter) => onTap((filter as TagFilter).tag),
|
onTap: onTap,
|
||||||
onLongPress: null,
|
onLongPress: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,26 @@
|
||||||
{
|
{
|
||||||
"de": [
|
"de": [
|
||||||
"editEntryLocationDialogSetCustom"
|
"editEntryLocationDialogSetCustom",
|
||||||
|
"tagEditorSectionPlaceholders",
|
||||||
|
"tagPlaceholderCountry",
|
||||||
|
"tagPlaceholderPlace"
|
||||||
],
|
],
|
||||||
|
|
||||||
"el": [
|
"el": [
|
||||||
"editEntryLocationDialogSetCustom"
|
"editEntryLocationDialogSetCustom",
|
||||||
|
"tagEditorSectionPlaceholders",
|
||||||
|
"tagPlaceholderCountry",
|
||||||
|
"tagPlaceholderPlace"
|
||||||
],
|
],
|
||||||
|
|
||||||
"es": [
|
"es": [
|
||||||
"widgetOpenPageHome",
|
"widgetOpenPageHome",
|
||||||
"widgetOpenPageCollection",
|
"widgetOpenPageCollection",
|
||||||
"widgetOpenPageViewer",
|
"widgetOpenPageViewer",
|
||||||
"editEntryLocationDialogSetCustom"
|
"editEntryLocationDialogSetCustom",
|
||||||
|
"tagEditorSectionPlaceholders",
|
||||||
|
"tagPlaceholderCountry",
|
||||||
|
"tagPlaceholderPlace"
|
||||||
],
|
],
|
||||||
|
|
||||||
"fa": [
|
"fa": [
|
||||||
|
@ -582,6 +591,9 @@
|
||||||
"tagEditorPageNewTagFieldLabel",
|
"tagEditorPageNewTagFieldLabel",
|
||||||
"tagEditorPageAddTagTooltip",
|
"tagEditorPageAddTagTooltip",
|
||||||
"tagEditorSectionRecent",
|
"tagEditorSectionRecent",
|
||||||
|
"tagEditorSectionPlaceholders",
|
||||||
|
"tagPlaceholderCountry",
|
||||||
|
"tagPlaceholderPlace",
|
||||||
"panoramaEnableSensorControl",
|
"panoramaEnableSensorControl",
|
||||||
"panoramaDisableSensorControl",
|
"panoramaDisableSensorControl",
|
||||||
"sourceViewerPageTitle",
|
"sourceViewerPageTitle",
|
||||||
|
@ -593,7 +605,10 @@
|
||||||
],
|
],
|
||||||
|
|
||||||
"fr": [
|
"fr": [
|
||||||
"editEntryLocationDialogSetCustom"
|
"editEntryLocationDialogSetCustom",
|
||||||
|
"tagEditorSectionPlaceholders",
|
||||||
|
"tagPlaceholderCountry",
|
||||||
|
"tagPlaceholderPlace"
|
||||||
],
|
],
|
||||||
|
|
||||||
"gl": [
|
"gl": [
|
||||||
|
@ -1031,6 +1046,9 @@
|
||||||
"tagEditorPageNewTagFieldLabel",
|
"tagEditorPageNewTagFieldLabel",
|
||||||
"tagEditorPageAddTagTooltip",
|
"tagEditorPageAddTagTooltip",
|
||||||
"tagEditorSectionRecent",
|
"tagEditorSectionRecent",
|
||||||
|
"tagEditorSectionPlaceholders",
|
||||||
|
"tagPlaceholderCountry",
|
||||||
|
"tagPlaceholderPlace",
|
||||||
"panoramaEnableSensorControl",
|
"panoramaEnableSensorControl",
|
||||||
"panoramaDisableSensorControl",
|
"panoramaDisableSensorControl",
|
||||||
"sourceViewerPageTitle",
|
"sourceViewerPageTitle",
|
||||||
|
@ -1057,11 +1075,17 @@
|
||||||
"settingsSlideshowAnimatedZoomEffect",
|
"settingsSlideshowAnimatedZoomEffect",
|
||||||
"settingsWidgetOpenPage",
|
"settingsWidgetOpenPage",
|
||||||
"statsTopAlbumsSectionTitle",
|
"statsTopAlbumsSectionTitle",
|
||||||
"wallpaperUseScrollEffect"
|
"wallpaperUseScrollEffect",
|
||||||
|
"tagEditorSectionPlaceholders",
|
||||||
|
"tagPlaceholderCountry",
|
||||||
|
"tagPlaceholderPlace"
|
||||||
],
|
],
|
||||||
|
|
||||||
"it": [
|
"it": [
|
||||||
"editEntryLocationDialogSetCustom"
|
"editEntryLocationDialogSetCustom",
|
||||||
|
"tagEditorSectionPlaceholders",
|
||||||
|
"tagPlaceholderCountry",
|
||||||
|
"tagPlaceholderPlace"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ja": [
|
"ja": [
|
||||||
|
@ -1097,11 +1121,17 @@
|
||||||
"settingsWidgetOpenPage",
|
"settingsWidgetOpenPage",
|
||||||
"statsTopAlbumsSectionTitle",
|
"statsTopAlbumsSectionTitle",
|
||||||
"viewerInfoLabelDescription",
|
"viewerInfoLabelDescription",
|
||||||
"wallpaperUseScrollEffect"
|
"wallpaperUseScrollEffect",
|
||||||
|
"tagEditorSectionPlaceholders",
|
||||||
|
"tagPlaceholderCountry",
|
||||||
|
"tagPlaceholderPlace"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ko": [
|
"ko": [
|
||||||
"editEntryLocationDialogSetCustom"
|
"editEntryLocationDialogSetCustom",
|
||||||
|
"tagEditorSectionPlaceholders",
|
||||||
|
"tagPlaceholderCountry",
|
||||||
|
"tagPlaceholderPlace"
|
||||||
],
|
],
|
||||||
|
|
||||||
"nb": [
|
"nb": [
|
||||||
|
@ -1210,7 +1240,10 @@
|
||||||
"mapEmptyRegion",
|
"mapEmptyRegion",
|
||||||
"viewerInfoOpenEmbeddedFailureFeedback",
|
"viewerInfoOpenEmbeddedFailureFeedback",
|
||||||
"viewerInfoSearchEmpty",
|
"viewerInfoSearchEmpty",
|
||||||
"wallpaperUseScrollEffect"
|
"wallpaperUseScrollEffect",
|
||||||
|
"tagEditorSectionPlaceholders",
|
||||||
|
"tagPlaceholderCountry",
|
||||||
|
"tagPlaceholderPlace"
|
||||||
],
|
],
|
||||||
|
|
||||||
"nl": [
|
"nl": [
|
||||||
|
@ -1219,7 +1252,10 @@
|
||||||
"durationDialogSeconds",
|
"durationDialogSeconds",
|
||||||
"editEntryLocationDialogSetCustom",
|
"editEntryLocationDialogSetCustom",
|
||||||
"aboutLinkPolicy",
|
"aboutLinkPolicy",
|
||||||
"policyPageTitle"
|
"policyPageTitle",
|
||||||
|
"tagEditorSectionPlaceholders",
|
||||||
|
"tagPlaceholderCountry",
|
||||||
|
"tagPlaceholderPlace"
|
||||||
],
|
],
|
||||||
|
|
||||||
"pl": [
|
"pl": [
|
||||||
|
@ -1697,6 +1733,9 @@
|
||||||
"tagEditorPageNewTagFieldLabel",
|
"tagEditorPageNewTagFieldLabel",
|
||||||
"tagEditorPageAddTagTooltip",
|
"tagEditorPageAddTagTooltip",
|
||||||
"tagEditorSectionRecent",
|
"tagEditorSectionRecent",
|
||||||
|
"tagEditorSectionPlaceholders",
|
||||||
|
"tagPlaceholderCountry",
|
||||||
|
"tagPlaceholderPlace",
|
||||||
"panoramaEnableSensorControl",
|
"panoramaEnableSensorControl",
|
||||||
"panoramaDisableSensorControl",
|
"panoramaDisableSensorControl",
|
||||||
"sourceViewerPageTitle",
|
"sourceViewerPageTitle",
|
||||||
|
@ -1708,11 +1747,17 @@
|
||||||
],
|
],
|
||||||
|
|
||||||
"pt": [
|
"pt": [
|
||||||
"editEntryLocationDialogSetCustom"
|
"editEntryLocationDialogSetCustom",
|
||||||
|
"tagEditorSectionPlaceholders",
|
||||||
|
"tagPlaceholderCountry",
|
||||||
|
"tagPlaceholderPlace"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ru": [
|
"ru": [
|
||||||
"editEntryLocationDialogSetCustom"
|
"editEntryLocationDialogSetCustom",
|
||||||
|
"tagEditorSectionPlaceholders",
|
||||||
|
"tagPlaceholderCountry",
|
||||||
|
"tagPlaceholderPlace"
|
||||||
],
|
],
|
||||||
|
|
||||||
"tr": [
|
"tr": [
|
||||||
|
@ -1774,10 +1819,16 @@
|
||||||
"statsTopAlbumsSectionTitle",
|
"statsTopAlbumsSectionTitle",
|
||||||
"viewerSetWallpaperButtonLabel",
|
"viewerSetWallpaperButtonLabel",
|
||||||
"viewerInfoLabelDescription",
|
"viewerInfoLabelDescription",
|
||||||
"wallpaperUseScrollEffect"
|
"wallpaperUseScrollEffect",
|
||||||
|
"tagEditorSectionPlaceholders",
|
||||||
|
"tagPlaceholderCountry",
|
||||||
|
"tagPlaceholderPlace"
|
||||||
],
|
],
|
||||||
|
|
||||||
"zh": [
|
"zh": [
|
||||||
"editEntryLocationDialogSetCustom"
|
"editEntryLocationDialogSetCustom",
|
||||||
|
"tagEditorSectionPlaceholders",
|
||||||
|
"tagPlaceholderCountry",
|
||||||
|
"tagPlaceholderPlace"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue