Skip to content

Commit dcfab1a

Browse files
kevinortiz43claude
andcommitted
Backend: remove invalid/deprecated ISO639-3 language codes
The allowed-languages list had three bad codes: `_tw` (invented; the official code is `nan`), `ajp` (deprecated, merged into `apc` per ISO 639-3 CR 2022-006), and `smi` (a part-2 group code, not an individual language). Remove them from languages.json and add migration 0170 to repoint existing user language abilities (`_tw`->`nan`, `ajp`->`apc`, drop `smi`), deduping to respect the unique (user_id, language_code) constraint. Adds a regression test that loads the real languages.json and asserts the codes are gone. Fixes #9172 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 0232469 commit dcfab1a

3 files changed

Lines changed: 64 additions & 12 deletions

File tree

app/backend/resources/languages.json

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
[
2-
{
3-
"code": "_tw",
4-
"name": "Taiwanese"
5-
},
62
{
73
"code": "aao",
84
"name": "Arabic (Algerian)"
@@ -55,10 +51,6 @@
5551
"code": "afr",
5652
"name": "Afrikaans"
5753
},
58-
{
59-
"code": "ajp",
60-
"name": "Arabic (Levantine South)"
61-
},
6254
{
6355
"code": "aka",
6456
"name": "Akan"
@@ -947,10 +939,6 @@
947939
"code": "slv",
948940
"name": "Slovenian"
949941
},
950-
{
951-
"code": "smi",
952-
"name": "Sámi"
953-
},
954942
{
955943
"code": "smo",
956944
"name": "Samoan"
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""Fix invalid/deprecated ISO 639-3 language codes
2+
3+
Revision ID: 0170
4+
Revises: 0169
5+
Create Date: 2026-06-29 00:00:00.000000
6+
7+
"""
8+
9+
from alembic import op
10+
from sqlalchemy.orm.session import Session
11+
12+
from couchers.resources import copy_resources_to_database
13+
14+
# revision identifiers, used by Alembic.
15+
revision = "0170"
16+
down_revision = "0169"
17+
branch_labels = None
18+
depends_on = None
19+
20+
21+
def upgrade() -> None:
22+
session = Session(bind=op.get_bind())
23+
# Reload languages table (removes _tw, ajp, smi from the static table)
24+
copy_resources_to_database(session)
25+
# _tw ("Taiwanese") is an invented code; official equivalent is nan ("Chinese (Southern Min)")
26+
# Dedupe first: drop _tw for users who already have nan
27+
op.execute(
28+
"DELETE FROM language_abilities WHERE language_code='_tw' AND user_id IN "
29+
"(SELECT user_id FROM language_abilities WHERE language_code='nan');"
30+
)
31+
op.execute("UPDATE language_abilities SET language_code='nan' WHERE language_code='_tw';")
32+
# ajp ("Arabic (Levantine South)") is deprecated; ISO merged it into apc ("Arabic (Levantine North)")
33+
# Dedupe first: drop ajp for users who already have apc
34+
op.execute(
35+
"DELETE FROM language_abilities WHERE language_code='ajp' AND user_id IN "
36+
"(SELECT user_id FROM language_abilities WHERE language_code='apc');"
37+
)
38+
op.execute("UPDATE language_abilities SET language_code='apc' WHERE language_code='ajp';")
39+
# smi ("Sámi") is an ISO 639-2 group code with no individual equivalent; drop it
40+
op.execute("DELETE FROM language_abilities WHERE language_code='smi';")
41+
session.commit()
42+
43+
44+
def downgrade() -> None:
45+
pass

app/backend/src/tests/test_resources.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import pytest
22
from google.protobuf import empty_pb2
3+
from sqlalchemy import select
34

5+
from couchers.db import session_scope
6+
from couchers.models import Language
7+
from couchers.resources import copy_resources_to_database
48
from tests.fixtures.sessions import resources_session
59

610

@@ -57,6 +61,21 @@ def test_GetLanguages(db):
5761
assert ("swe", "Swedish") not in languages_list
5862

5963

64+
def test_languages_resource_excludes_invalid_codes(db):
65+
# Load the real languages.json (not the hardcoded testing fixture) so a future bad code is caught.
66+
# Mirrors test_add_dummy_data's pattern of calling copy_resources_to_database directly.
67+
with session_scope() as session:
68+
copy_resources_to_database(session)
69+
codes = set(session.execute(select(Language.code)).scalars().all())
70+
# Codes removed in #9172: invalid/deprecated ISO 639-3 entries
71+
assert "_tw" not in codes # invented code; use nan (Chinese Southern Min) instead
72+
assert "ajp" not in codes # deprecated; ISO merged into apc (Arabic Levantine North)
73+
assert "smi" not in codes # ISO 639-2 group code with no individual equivalent
74+
# Remap targets must remain present
75+
assert "nan" in codes
76+
assert "apc" in codes
77+
78+
6079
def test_GetBadges(db):
6180
with resources_session() as api:
6281
badges = api.GetBadges(empty_pb2.Empty()).badges

0 commit comments

Comments
 (0)