Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions branding/i18n/overrides-en.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"asset_offline_description": "This external library asset is no longer found on disk and has been moved to trash. If the file was moved within the library, check your timeline for the new corresponding asset. To restore this asset, please ensure that the file path below can be accessed by Noodle Gallery and scan the library.",
"admin": {
"asset_offline_description": "This external library asset is no longer found on disk and has been moved to trash. If the file was moved within the library, check your timeline for the new corresponding asset. To restore this asset, please ensure that the file path below can be accessed by Noodle Gallery and scan the library.",
"backup_onboarding_description": "A <backblaze-link>3-2-1 backup strategy</backblaze-link> is recommended to protect your data. You should keep copies of your uploaded photos/videos as well as the Noodle Gallery database for a comprehensive backup solution.",
"backup_onboarding_footer": "For more information about backing up Noodle Gallery, please refer to the <link>documentation</link>.",
"confirm_delete_library_assets": "Are you sure you want to delete this library? This will delete {count, plural, one {# contained asset} other {all # contained assets}} from Noodle Gallery and cannot be undone. Files will remain on disk.",
Expand All @@ -15,6 +15,7 @@
"theme_settings_description": "Manage customization of the Noodle Gallery web interface"
},
"advanced_settings_proxy_headers_subtitle": "Define proxy headers Noodle Gallery should send with each network request",
"asset_offline_description": "This external asset is no longer found on disk. Please contact your Noodle Gallery administrator for help.",
"assets_deleted_permanently_from_server": "{count} asset(s) deleted permanently from the Noodle Gallery server",
"assets_trashed_from_server": "{count} asset(s) trashed from the Noodle Gallery server",
"background_location_permission_content": "Noodle Gallery uses background location permission to keep reading the Wi-Fi network name while automatic server switching runs in the background. The Wi-Fi network name is saved on this device for matching, and your precise device location is not stored or shared.",
Expand All @@ -33,13 +34,18 @@
"ignore_icloud_photos_description": "Photos that are stored on iCloud will not be uploaded to the Noodle Gallery server",
"immich_logo": "Noodle Gallery Logo",
"immich_web_interface": "Noodle Gallery Web Interface",
"import_option_skip_duplicates": "Skip duplicates already in Noodle Gallery",
"install_app_title": "Install Noodle Gallery",
"location_permission_content": "Android requires precise location permission so Noodle Gallery can read the current Wi-Fi network name for automatic server switching. The Wi-Fi network name is saved on this device for matching, and your precise device location is not stored or shared.",
"maintenance_description": "Noodle Gallery has been put into <link>maintenance mode</link>.",
"manage_media_access_subtitle": "Allow the Noodle Gallery app to manage and move media files.",
"my_immich_description": "Copy current page as a my.immich.app link",
"my_immich_title": "my.immich.app link",
"notification_enabled_list_tile_content": "Noodle Gallery uses notifications for background backup. Manage them in your device settings.",
"obtainium_configurator_instructions": "Use Obtainium to install and update the Android app directly from Noodle Gallery GitHub's release. Create an API key and select a variant to create your Obtainium configuration link",
"ocr_body": "Noodle Gallery now reads the text inside your photos, so you can search for them by what they say.",
"official_immich_resources": "Official Noodle Gallery Resources",
"open_in_app_banner_title": "Open in Noodle Gallery",
"open_in_immich_body": "Set Noodle Gallery as your gallery on Android to open photos straight from other apps.",
"open_in_immich_title": "Open photos in Noodle Gallery",
"permission_onboarding_permission_denied": "Permission denied. To use Noodle Gallery, grant photo and video permissions in Settings.",
Expand All @@ -63,10 +69,8 @@
"sync_upload_album_setting_subtitle": "Create and upload your photos and videos to the selected albums on Noodle Gallery",
"trash_page_empty_trash_dialog_content": "Do you want to empty your trashed assets? These items will be permanently removed from Noodle Gallery",
"upload_to_immich": "Upload to Noodle Gallery ({count})",
"whats_new_settings_subtitle": "See what's new in Noodle Gallery",
"version_announcement_closing": "Your friend, Pierre",
"version_announcement_message": "Hi there! A new version of Noodle Gallery is available. Please take some time to read the <link>release notes</link> to ensure your setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your Noodle Gallery instance automatically.",
"welcome_to_immich": "Welcome to Noodle Gallery",
"version_announcement_closing": "Your friend, Pierre",
"open_in_app_banner_title": "Open in Noodle Gallery",
"install_app_title": "Install Noodle Gallery"
"whats_new_settings_subtitle": "See what's new in Noodle Gallery"
}
33 changes: 33 additions & 0 deletions branding/scripts/test-i18n-branding.sh
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,39 @@ done
absent_at de admin.theme_settings_description 'de drops admin.theme_settings_description -> falls back to en'
absent_at fr admin.maintenance_settings_description 'fr drops admin.maintenance_settings_description -> falls back to en'

# Issue #743 item 4: asset_offline_description exists BOTH top-level and under
# .admin — a top-level-only override recreates the #672 bug shape (the override
# clobbers the user-facing key with the admin text while admin.* keeps leaking).
echo "asset_offline_description branded at BOTH nesting levels (issue #743):"
branded_at en admin.asset_offline_description 'en admin.asset_offline_description branded under .admin'
top_offline=$(val_at en asset_offline_description)
if echo "$top_offline" | grep -q "$UPSTREAM_NAME"; then
echo " FAIL: en top-level asset_offline_description still contains '$UPSTREAM_NAME': '$top_offline'"
fails=$((fails + 1))
elif ! echo "$top_offline" | grep -q "administrator"; then
echo " FAIL: en top-level asset_offline_description lost its user-facing text (got the admin text?): '$top_offline'"
fails=$((fails + 1))
else
echo " ok: en top-level asset_offline_description keeps its user-facing text, branded"
fi

# Issue #743 item 4: the override-driven checks above only cover keys that HAVE
# overrides. Scan every string value in the branded en.json so a missing override
# can never pass silently again.
echo "Whole-file scan: branded en.json leaks the upstream name nowhere:"
en_leaks=$(jq -r --arg up "$UPSTREAM_NAME" '
paths(scalars) as $p
| select((getpath($p) | type) == "string" and (getpath($p) | contains($up)))
| ($p | join("."))' "$TMP/i18n/en.json")
if [[ -n "$en_leaks" ]]; then
while IFS= read -r k; do
echo " FAIL: en.json still contains '$UPSTREAM_NAME' in key '$k' (missing from overrides-en.json)"
fails=$((fails + 1))
done <<<"$en_leaks"
else
echo " ok: 0 upstream-name values in branded en.json"
fi

echo "Unrelated localized strings are preserved (no collateral damage):"
# 'albums' is a generic key the fork does not rebrand; it must keep its German value.
de_albums=$(jq -r '.albums // " ABSENT"' "$TMP/i18n/de.json")
Expand Down
17 changes: 17 additions & 0 deletions branding/scripts/verify-branding.sh
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,23 @@ if [[ -f "$overrides_file" && -f "$i18n_file" ]]; then
done < <(jq -r '[paths(scalars)] | .[] | join(".")' "$overrides_file")
echo " i18n: $((override_count - leaked))/$override_count keys patched"

# Issue #743: the loop above only checks keys that HAVE overrides — a leaking
# key missing from overrides-en.json passed silently. Scan every string value
# in the branded en.json so that class can't slip through again.
en_leaks=0
while IFS= read -r keypath; do
[[ -n "$keypath" ]] || continue
echo " WARN: i18n key '$keypath' contains '$UPSTREAM_NAME' but has no override in overrides-en.json"
en_leaks=$((en_leaks + 1))
EXIT_CODE=1
done < <(jq -r --arg up "$UPSTREAM_NAME" '
paths(scalars) as $p
| select((getpath($p) | type) == "string" and (getpath($p) | contains($up)))
| ($p | join("."))' "$i18n_file")
if [[ $en_leaks -eq 0 ]]; then
echo " i18n: no unbranded '$UPSTREAM_NAME' values remain in en.json"
fi

# Issue #703: the upstream name must not leak through *any* locale for a key
# the fork rebrands. Non-English locales carry upstream Weblate translations
# of these keys; patch_i18n() either replaces them with a per-locale override
Expand Down
32 changes: 16 additions & 16 deletions mobile/openapi/lib/model/time_bucket_asset_response_dto.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion open-api/bin/generate-dart-sdk.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ rm -rf ../mobile/openapi
cd ./templates/mobile/serialization/native
wget -O native_class.mustache https://raw.githubusercontent.com/OpenAPITools/openapi-generator/$OPENAPI_GENERATOR_VERSION/modules/openapi-generator/src/main/resources/dart2/serialization/native/native_class.mustache
patch --no-backup-if-mismatch -u native_class.mustache <native_class.mustache.patch
# Must apply AFTER native_class.mustache.patch — its hunks are authored against the
# patched template. Types nullable-item arrays as List<T?> (issue #743 item 3).
patch --no-backup-if-mismatch -u native_class.mustache <native_class_nullable_items_in_arrays.patch

cd ../../
wget -O api.mustache https://raw.githubusercontent.com/OpenAPITools/openapi-generator/$OPENAPI_GENERATOR_VERSION/modules/openapi-generator/src/main/resources/dart2/api.mustache
Expand All @@ -23,7 +26,6 @@ patch --no-backup-if-mismatch -u ../mobile/openapi/lib/api_client.dart <./patch/
patch --no-backup-if-mismatch -u ../mobile/openapi/lib/api.dart <./patch/api.dart.patch
patch --no-backup-if-mismatch -u ../mobile/openapi/pubspec.yaml <./patch/pubspec_immich_mobile.yaml.patch
patch --no-backup-if-mismatch -u ../mobile/openapi/lib/model/asset_edit_action_item_dto.dart <./patch/asset_edit_action_item_dto.dart.patch
patch --no-backup-if-mismatch -u ../mobile/openapi/lib/model/time_bucket_asset_response_dto.dart <./patch/time_bucket_asset_response_dto.dart.patch
# Don't include analysis_options.yaml for the generated openapi files
# so that language servers can properly exclude the mobile/openapi directory
rm ../mobile/openapi/analysis_options.yaml
9 changes: 0 additions & 9 deletions open-api/patch/time_bucket_asset_response_dto.dart.patch

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,44 @@ class {{{classname}}} {
{{/isNullable}}
{{/isEnum}}
{{#required}}
{{#isArray}}
{{#items.isNullable}}
{{#uniqueItems}}Set{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}<{{{items.datatypeWithEnum}}}?>{{#isNullable}}?{{/isNullable}} {{{name}}};
{{/items.isNullable}}
{{^items.isNullable}}
{{{datatypeWithEnum}}}{{#isNullable}}?{{/isNullable}} {{{name}}};
{{/items.isNullable}}
{{/isArray}}
{{^isArray}}
{{{datatypeWithEnum}}}{{#isNullable}}?{{/isNullable}} {{{name}}};
{{/isArray}}
{{/required}}
{{^required}}
{{#vendorExtensions.x-is-optional}}
{{#isArray}}
{{#items.isNullable}}
Optional<{{#uniqueItems}}Set{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}<{{{items.datatypeWithEnum}}}?>?> {{{name}}};
{{/items.isNullable}}
{{^items.isNullable}}
{{{datatypeWithEnum}}} {{{name}}};
{{/items.isNullable}}
{{/isArray}}
{{^isArray}}
{{{datatypeWithEnum}}} {{{name}}};
{{/isArray}}
{{/vendorExtensions.x-is-optional}}
{{^vendorExtensions.x-is-optional}}
{{#isArray}}
{{#items.isNullable}}
{{#uniqueItems}}Set{{/uniqueItems}}{{^uniqueItems}}List{{/uniqueItems}}<{{{items.datatypeWithEnum}}}?>{{#isNullable}}?{{/isNullable}}{{^isNullable}}{{^defaultValue}}?{{/defaultValue}}{{/isNullable}} {{{name}}};
{{/items.isNullable}}
{{^items.isNullable}}
{{{datatypeWithEnum}}}{{#isNullable}}?{{/isNullable}}{{^isNullable}}{{^defaultValue}}?{{/defaultValue}}{{/isNullable}} {{{name}}};
{{/items.isNullable}}
{{/isArray}}
{{^isArray}}
{{{datatypeWithEnum}}}{{#isNullable}}?{{/isNullable}}{{^isNullable}}{{^defaultValue}}?{{/defaultValue}}{{/isNullable}} {{{name}}};
{{/isArray}}
{{/vendorExtensions.x-is-optional}}
{{/required}}

Expand Down Expand Up @@ -305,12 +335,12 @@ class {{{classname}}} {
{{^isEnum}}
{{#vendorExtensions.x-is-optional}}
{{{name}}}: json.containsKey(r'{{{baseName}}}') ? Optional.present(json[r'{{{baseName}}}'] is Iterable
? (json[r'{{{baseName}}}'] as Iterable).cast<{{{items.datatype}}}>().{{#uniqueItems}}toSet(){{/uniqueItems}}{{^uniqueItems}}toList(growable: false){{/uniqueItems}}
? (json[r'{{{baseName}}}'] as Iterable).cast<{{{items.datatype}}}{{#items.isNullable}}?{{/items.isNullable}}>().{{#uniqueItems}}toSet(){{/uniqueItems}}{{^uniqueItems}}toList(growable: false){{/uniqueItems}}
: {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}}) : const Optional.absent(),
{{/vendorExtensions.x-is-optional}}
{{^vendorExtensions.x-is-optional}}
{{{name}}}: json[r'{{{baseName}}}'] is Iterable
? (json[r'{{{baseName}}}'] as Iterable).cast<{{{items.datatype}}}>().{{#uniqueItems}}toSet(){{/uniqueItems}}{{^uniqueItems}}toList(growable: false){{/uniqueItems}}
? (json[r'{{{baseName}}}'] as Iterable).cast<{{{items.datatype}}}{{#items.isNullable}}?{{/items.isNullable}}>().{{#uniqueItems}}toSet(){{/uniqueItems}}{{^uniqueItems}}toList(growable: false){{/uniqueItems}}
: {{#defaultValue}}{{{.}}}{{/defaultValue}}{{^defaultValue}}null{{/defaultValue}},
{{/vendorExtensions.x-is-optional}}
{{/isEnum}}
Expand Down
Loading
Loading